﻿#define TODO
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using common;

namespace client
{
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// <summary>
    ///  PEMS Kliens
    /// </summary>
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////
    public sealed class Kliens
    {
        #region Variables

        //az egyke
        private static Kliens _instance = null;

        //singleton szálbiztos kezeléséhez kell
        private static readonly Object _lock = new Object();

        private static common.KliensID _kliensID = new common.KliensID();

        //az automatikus felcsatlakozás miatt
        private static System.Timers.Timer _connecttimer = new System.Timers.Timer();

        private static TcpClient _client = null;

        private static IPHostEntry _ipEntry = null;

        private static Thread _clientThread = null;

        private static IPEndPoint _serverEndPoint = null;
        #endregion

        #region Constructors
        /// <summary>
        ///  Default constructor
        /// </summary>
        public Kliens()
        {
            Log.logToFile("----Kliens constructor----", false);
            _client = new TcpClient();
            _connecttimer.Enabled = true;
            _connecttimer.Interval = 1000;
            _connecttimer.Elapsed += new System.Timers.ElapsedEventHandler(this.connecttimer_Elapsed);
            setipEntry1();
        }
        #endregion

        #region Properties
        /// <summary>
        /// az egyke legyártása (egyszer, (lock alatt)) és visszaadása
        /// </summary>
        public static Kliens instance
        {
            get
            {
                lock (_lock)
                {
                    if (_instance == null)
                    {
                        _instance = new Kliens();
                    }
                }

                return _instance;
            }
        }
        #endregion

        #region Methods
        /// <summary>
        /// az egyke megpróbál tcp/ip-n csatlakozni, ha csatlakozott, utána authentikálni
        /// </summary>
        public void init()
        {
            clientConnectToServer();

            Console.Clear();

            while (true)
            {
                if (_kliensID.id != 0)
                {
                    Console.CursorVisible = true;
                    Log.logToFile("connected", false);
                    if (!_kliensID.belepve)
                        login();
                    else
                        menu();
                }
                else
                {
                    Console.CursorVisible = false;
                    Console.SetCursorPosition(0, 0);
                    Console.Write("connecting to PEMS server...");
                }
                Thread.Sleep(200);
            }
        }
        /// <summary>
        /// felhasználó azonosítás, az eredményt visszaküldjük hálózaton
        /// </summary>
        static void login()
        {
            header();
            try
            {
                Console.Write("username: ");
                _kliensID.userNev = Console.ReadLine();
                Console.Write("password: ");
                _kliensID.jelszo = getPassword();
                sendObject(_kliensID);
            }
            catch
            {
                Console.WriteLine("ERROR");
            }
        }
        /// <summary>
        /// jelszó bekérés, úgy hogy ne látszódjanak a beírt karakterek
        /// </summary>
        /// <returns>a jelszó</returns>
        public static string getPassword()
        {
            string password = "";
            ConsoleKeyInfo info = Console.ReadKey(true);
            while (info.Key != ConsoleKey.Enter)
            {
                if (info.Key != ConsoleKey.Backspace)
                {
                    password += info.KeyChar;
                    info = Console.ReadKey(true);
                }
                else if (info.Key == ConsoleKey.Backspace)
                {
                    if (!string.IsNullOrEmpty(password))
                    {
                        password = password.Substring
                        (0, password.Length - 1);
                    }
                    info = Console.ReadKey(true);
                }
            }
            for (int i = 0; i < password.Length; i++)
                Console.Write("*");
            return password;
        }
        /// <summary>
        ///a főmenü
        /// </summary>
        static void menu()
        {
            const int maxMenuItems = 2;
            int selector = 0;
            bool good = false;

            while (selector != maxMenuItems)
            {
                header();
                Console.WriteLine("1: teszt üzenet");
                Console.WriteLine("2: disconnect");
                good = int.TryParse(Console.ReadLine(), out selector);

                if (_kliensID.id == 0)
                    break;
                if (!_kliensID.belepve)
                    break;



                if (good)
                {
                    switch (selector)
                    {
                        case 1:
                            sendObject("Ez egy kliens üzenet!");
                            break;
                        case 2:
                            Console.Clear();
                            disconnect();
                            Thread.Sleep(1000);
                            break;
                        default:
                            break;
                    }
                }
                else
                {

                }
                //Console.ReadKey();
            }
        }

        /// <summary>
        /// konzok ablak első két sora
        /// </summary>
        static void header()
        {
            int consoleWidth = Console.WindowWidth;
            Console.Clear();
            Console.SetCursorPosition(0, 0);
            Console.Write("PEMS");
            Console.SetCursorPosition(20, 0);
            Console.Write("ID: {0}", _kliensID.id);
            Console.SetCursorPosition(40, 0);
            if (_kliensID.belepve)
                Console.Write("user: {0}", _kliensID.userNev);
            Console.WriteLine("");

            for (int i = 0; i < consoleWidth; i++)
            {
                Console.Write("-");
            }
            Console.WriteLine("");

        }

        /// <summary>
        /// az automatikus csatlakozás
        /// </summary>
        private void connecttimer_Elapsed(object sender, EventArgs e)
        {
            if (Properties.Settings.Default.autoconnect)
            {
                if ((_client != null) && (_client.Connected)) { return; }
                clientConnectToServer();
            }

        }

        /// <summary>
        /// a hálózatról jövő adatok figyelése, szálban (végtelen ciklus)
        /// </summary>
        /// <param name="client">ezt a TcpClient-t figyeljük</param>
        private void handleComm(object client)
        {
            
            TcpClient tcpClient = (TcpClient)client;
            Thread.Sleep(200);
            NetworkStream clientStream;

            try
            {
                clientStream = tcpClient.GetStream();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return;
            }

            while (true)
            {
                try
                {
                    IFormatter formatter = new BinaryFormatter();

                    common.NetworkMessage networkmessage = (NetworkMessage)formatter.Deserialize(clientStream);

                    erkezettadatfeldolgozas(networkmessage);
                }
                catch
                {
                    //Console.WriteLine(e);
                    //Console.WriteLine("Disconnected");
                    _client.Close();
                    _client = null;
                    break;

                }



            }
        }
        /// <summary>
        /// a bejövő adatok kezelése
        /// </summary>
        /// <param name="nm">ezt jött hálózaton</param>
        private void erkezettadatfeldolgozas(NetworkMessage nm)
        {
            MemoryStream ms=new MemoryStream(nm.objData);
            BinaryFormatter bf=new BinaryFormatter();
            object o = (object)bf.Deserialize(ms);
            ms.Close();

            //Console.WriteLine("incoming object: " + nm.objTipus);

            if (nm.objTipus == typeof(String))
            {
                Log.logToFile((string)o, false);
                
                if ((string)o == "auth_failed")
                {
                    _kliensID.belepve = false;

                }
                
                if ((string)o == "auth_oké")
                {
                    _kliensID.belepve = true;
                }
            }

            if (nm.objTipus == typeof(common.KliensID))
            {
                _kliensID.id = ((common.KliensID)o).id;
                Log.logToFile("ID a szerveren: " + _kliensID.id, true);
            }
        }
        /// <summary>
        /// a távoli végpont felderítése
        /// </summary>
        private bool setServerEndPoint()
        {
            
            //serverEndPoint = null;
            try
            {
                IPAddress[] addr = _ipEntry.AddressList;


                foreach (IPAddress ad in addr)
                {
                    if (ad.AddressFamily == AddressFamily.InterNetwork)
                    {
                        _serverEndPoint = new IPEndPoint(ad, Properties.Settings.Default.remoteport);
                        Log.logToFile("Kapcsolódás végpontja: " + ad.ToString() + ":" + Properties.Settings.Default.remoteport.ToString(), false);
                        return true;
                    }
                }
            }
            catch
            {
                Log.logToFile("Kapcsolódás végpontja még nem meghatározott: " + Properties.Settings.Default.remotehost +":"+ Properties.Settings.Default.remoteport,false);
            }
            return false;
        }
        /// <summary>
        /// külön szálban indítjuk a dns lekérdezést, mert időigényes lehet
        /// </summary>
        private void setipEntry1()
        {
            Thread clientinitThread = new Thread(setipEntry);
            clientinitThread.IsBackground = false;
            clientinitThread.Start();
        }

        /// <summary>
        /// a dns lekérdezés
        /// </summary>
        private void setipEntry()
        {
            try
            {
                _ipEntry = null;
                _ipEntry = System.Net.Dns.GetHostEntry(Properties.Settings.Default.remotehost);
                //Console.WriteLine("Távoli gép azonosítva: {0}", Properties.Settings.Default.remotehost);
            }
            catch
            {
                Console.WriteLine("Nem lehet a távoli gépet azonosítani: {0}", Properties.Settings.Default.remotehost);
            }
            
        }

        /// <summary>
        /// a tényleges csatlakozás
        /// </summary>
        public void clientConnectToServer()
        {

            if ((_client != null) && (_client.Connected)) { Console.WriteLine("már van kapcsolat"); return; }

            if (!setServerEndPoint()) {
                //Console.WriteLine("Nem sikerult a távoli csatlakozási pont azonosítása.");
                
                return; 
            }

            try
            {
                if (_client == null) _client = new TcpClient();
                _client.Connect(_serverEndPoint);
            }
            catch
            {
                //_client = null;
                _kliensID.id = 0;
                Console.Clear();
                Console.CursorVisible = false;
                Console.WriteLine("connecting to PEMS server...");

                return;
            }
            //sikerult konnektalni
            Console.CursorVisible = true;
            _kliensID.belepve = false;
            Console.Clear();
            Console.WriteLine("PEMS login: Enter");

            _clientThread = new Thread(new ParameterizedThreadStart(handleComm));
            _clientThread.Name = "listenThread";
            _clientThread.IsBackground = false;
            _clientThread.Start(_client);

            //eloszor kuldunk egy PCid objektumot (logon)
            _kliensID.ip = _client.Client.LocalEndPoint.ToString();
            _kliensID.gepNev = Environment.MachineName;
            sendObject(_kliensID);   

        }
        /// <summary>
        /// Hálózaton adatot küldünk
        /// </summary>
        /// <param name="o">ezt az adatot küldjük</param>
        public static void sendObject(object o)
        {
            //Console.WriteLine("outging object: " + o.GetType().ToString());

            NetworkStream nstream = null;
            try
            {
                nstream = _client.GetStream();
            }
            catch {
                Console.WriteLine("nincs network stream...");
                return;
            }

            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf = new BinaryFormatter();



            bf.Serialize(ms, o);
            byte[] data = ms.GetBuffer();
            ms.Close();


            common.NetworkMessage üzenet = new common.NetworkMessage(o.GetType(), data);


            IFormatter formatter = new BinaryFormatter();
            try
            {
                formatter.Serialize(nstream, üzenet);
            }
            catch
            {
                Console.WriteLine("NetworkMessage send failed");
            }

        }
        /// <summary>
        /// hálózat lecsatlakozás
        /// </summary>
        public static void disconnect()
        {
            try
            {
                //Console.WriteLine("disconnecting...");

                if ((_client != null) && (_client.Connected))
                {
                    _client.Close();
                }
            }
            catch
            {
                Console.WriteLine("disconnect error...");
                return;
            }
        }
        #endregion
    }
}
